﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.IO.Compression;

namespace Mesuro.DataManagement {
	public class DataExtractor {
		private PayloadExtract[] payload;
		public ExperimentHeader fileHeader { get; private set; }
		private string destinationDir;
		public static ExperimentHeader Parse( FileInfo sourceFile, string outputDir = null ) {
			return (new DataExtractor( sourceFile, outputDir )).fileHeader;
		}
		private DataExtractor( FileInfo sourceFile, string destination = null ) {
			fileHeader = new ExperimentHeader();
			//CREATES A DIRECTORY to store Payload,metadata, Var files
			this.destinationDir = destination ?? sourceFile.DirectoryName;

			System.IO.Directory.CreateDirectory( this.destinationDir );

			fileHeader.Description = sourceFile.Name;
			fileHeader.MetadataPath = destinationDir + @"\metadata.xml";

			payload = new PayloadExtract[2];
			payload[0] = new PayloadExtract();
			payload[1] = new PayloadExtract();
			readFile( sourceFile );
			createMetadata();
		}
		void readFile( FileInfo sourceFile ) {
			payload[0].location = this.destinationDir + @"\var.csv";
			payload[1].location = this.destinationDir + @"\payload.csv";

			//CREATING FILES
			StreamWriter varFile = File.CreateText( payload[0].location );
			StreamWriter payloadFile = File.CreateText( payload[1].location );
			using (StreamReader sweepFile = sourceFile.OpenText()) {
				string line = "";
				string[] headerSeparator = new string[] { "!", "Date/Time of measurement\t\t", "Frequency Frequency\t\t", 
                                                     "Valid harmonics\t\t\t", "Valid _harmonics\t\t\t", "DC Voltage - Port1\t\t", 
                                                     "DC Voltage - Port2\t\t", "AC Voltage - Port1\t\t", "AC Voltage - Port2\t\t" };
				string[] staticColumns ={"a1-real","a1-img","b1-real","b1-img","a2-real","a2-img","b2-real","b2-img","v1-real","v1-img",
                                        "i1-real","i1-img","v2-real","v2-img","i2-real","i2-img"};
				bool varControl = true;
				string[] varColumns = new string[1];
				int varIndex = 0;

				double[] varMin = new double[1];
				double[] varMax = new double[1];
				int varParIndex = 0;
				double[] varMean = new double[1];
				int varRows = 0;

				payload[1].columns = staticColumns;

				double[] payloadMin = new double[staticColumns.Length]; int payIndex = 0;//counts upto 16 then loops back
				double[] payloadMax = new double[staticColumns.Length];
				double[] payloadMean = new double[staticColumns.Length];
				int payloadRows = 0;

				while ((line = sweepFile.ReadLine()) != null) {
					if (line.StartsWith( "!" ))//initialize file headers
                {
						if (line.Contains( "Date/Time" )) {
							string[] temp = line.Split( headerSeparator, StringSplitOptions.None );

							foreach (string time in temp) {
								if (!(String.IsNullOrWhiteSpace( time ))) {
									fileHeader.Timestamp = time;
								}
							}
						} else if (line.Contains( "Frequency Frequency" )) {
							fileHeader.Frequency = returnValue2( line, headerSeparator );
						} else if (line.Contains( "Valid harmonics" ) || line.Contains( "Valid _harmonics" )) {
							fileHeader.Harmonics = returnValue( line, headerSeparator );
						} else if (line.Contains( "Port1" )) {
							fileHeader.Bias1 = returnValue( line, headerSeparator );
							if (line.Contains( "DC Voltage" )) {
								fileHeader.DcMeasured = 1;
							} else//AC MEASURED
                        {
								fileHeader.DcMeasured = 0;
							}
						} else if (line.Contains( "Port2" )) {
							fileHeader.Bias2 = returnValue( line, headerSeparator );
						}
					}//End initializing fileheader
					if (line.StartsWith( "VAR" ))//get VARS
                {
						while (!line.Contains( "BEGIN" )) {
							int x = line.IndexOf( 'R' );
							int y = line.IndexOf( '=' );

							varFile.Write( line.Substring( y + 1 ) + "," );
							double varValue;
							Double.TryParse( line.Substring( y + 1 ), out varValue );

							//Var headings
							if (varControl)//write once 
                        {
								varColumns[varIndex] = line.Substring( x + 1, y - 3 ).Trim();
								varMax[varIndex] = varValue;
								varMin[varIndex] = varValue;
								varMean[varIndex] = varValue;
								varIndex++;
								Array.Resize<double>( ref varMax, varIndex + 1 );//Resize the var arrays
								Array.Resize<double>( ref varMin, varIndex + 1 );
								Array.Resize<double>( ref varMean, varIndex + 1 );
								Array.Resize<string>( ref varColumns, varIndex + 1 );
							} else {
								varMax[varParIndex] = max( varValue, varMax[varParIndex] );
								varMin[varParIndex] = min( varValue, varMin[varParIndex] );
								varMean[varParIndex] = varMean[varParIndex] + varValue;
								varParIndex++;
							}
							line = sweepFile.ReadLine();
						}
						varRows++;
					}//END VARs 
					if (line.EndsWith( "i2(complex)" ))//get Datablocks
                {
						varFile.Write( "\n" );
						varControl = false;
						varParIndex = 0;
						line = sweepFile.ReadLine();
						while (!line.StartsWith( "END" ))//Column B-I shouldnt be stored
                    {
							payloadRows++;
							string[] temp = line.Split( '\t' );

							for (int k = 1; k < temp.Length; k++) {
								payloadFile.Write( temp[k] + "," );
								if (payIndex < payload[1].columns.Length) {
									double x;
									Double.TryParse( temp[k], out x );
									payloadMin[payIndex] = min( payloadMin[payIndex], x );
									payloadMax[payIndex] = max( payloadMax[payIndex], x );
									payloadMean[payIndex] = payloadMean[payIndex] + x;
									payIndex++;
								}
							}
							payloadFile.Write( "\n" );
							payIndex = 0;
							line = sweepFile.ReadLine();
						}
					}
				}//End while
				payload[0].columns = varColumns;
				payload[0].min = varMin;
				payload[0].max = varMax;

				double[] vMean = new double[varMean.Length];

				for (int k = 0; k < varMean.Length; k++) {
					vMean[k] = varMean[k] / varRows;
				}
				payload[0].mean = vMean;

				payload[1].min = payloadMin;
				payload[1].max = payloadMax;

				double[] pMean = new double[payloadMean.Length];
				for (int k = 0; k < pMean.Length; k++) {
					pMean[k] = payloadMean[k] / payloadRows;
				}
				payload[1].mean = pMean;
			}//End of using a file
			payloadFile.Close();
			varFile.Close();
		}
		/**
	 * returnValue(string,string []) method gets the int value of a string
	 * @lso see getHeaderInfo
	 */
		int returnValue( string st, string[] stSeparator ) {
			int x = -1;
			string[] temp = st.Split( stSeparator, StringSplitOptions.None );
			foreach (string s in temp) {
				if (!(String.IsNullOrWhiteSpace( s ))) {
					int j = 0;

					for (int i = 0; i < s.Length; i++) {
						if (Char.IsNumber( s, i )) {
							j = i;
						}
					}
					if (int.TryParse( s.Substring( 0, j + 1 ), out x )) {
						return x;
					}
				}
			}
			return x;
		}
		/**
		 * returnValue2(string,string []) returns a double value of the input string
		 * @lso see getHeaderInfo(string,string)
		 */
		double returnValue2( string st, string[] stSeparator ) {
			double x = -1;
			string[] temp = st.Split( stSeparator, StringSplitOptions.None );
			foreach (string s in temp) {
				if (!(String.IsNullOrWhiteSpace( s ))) {
					int j = 0;

					for (int i = 0; i < s.Length; i++) {
						if (Char.IsNumber( s, i )) {
							j = i;
						}
					}
					if (Double.TryParse( s.Substring( 0, j + 1 ), out x )) {
						return x;
					}
				}
			}
			return x;
		}
		double min( double min, double x ) {
			if (min > x) {
				return x;
			}
			return min;
		}
		double max( double max, double x ) {
			if (max < x) {
				return x;
			}
			return max;
		}
		void createMetadata() {
			string fileloc = fileHeader.MetadataPath;
			XmlTextWriter metadataXml = new XmlTextWriter( fileloc, System.Text.Encoding.UTF8 );
			try {
				metadataXml.WriteStartDocument();
				metadataXml.Formatting = Formatting.Indented;
				metadataXml.WriteStartElement( "mesuro" );
				metadataXml.WriteAttributeString( "xmlns", "mesuro", null, fileloc );
				//FILE HEADER
				metadataXml.WriteStartElement( "File-header", fileloc );
				//metadataXml.WriteStartElement( "SourceFile", fileloc );
				//metadataXml.WriteString( fileHeader.sourceFile );
				//metadataXml.WriteEndElement();
				metadataXml.WriteStartElement( "Data-Acquisation-Software", fileloc );
				metadataXml.WriteString( "C#" );
				metadataXml.WriteEndElement();
				metadataXml.WriteStartElement( "operator", fileloc );
				metadataXml.WriteString( "Anonymous" );
				metadataXml.WriteEndElement();
				metadataXml.WriteStartElement( "Timeline", fileloc );
				metadataXml.WriteString( fileHeader.Timestamp );
				metadataXml.WriteEndElement();
				metadataXml.WriteStartElement( "Frequency-Frequency", fileloc );
				metadataXml.WriteString( fileHeader.Frequency + "" );
				metadataXml.WriteEndElement();
				metadataXml.WriteStartElement( "Num-of-harmonics", fileloc );
				metadataXml.WriteString( fileHeader.Harmonics + "" );
				metadataXml.WriteEndElement();
				metadataXml.WriteEndElement();
				//----------------------------------VARS----------------------------------------------
				metadataXml.WriteStartElement( "payload", fileloc );
				metadataXml.WriteAttributeString( "location", null, payload[0].location );
				metadataXml.WriteAttributeString( "version", null, "1.0" );
				metadataXml.WriteAttributeString( "type", null, "csv" );

				string[] columns = payload[0].columns;
				double[] min = payload[0].min;
				double[] max = payload[0].max;
				double[] mean = payload[0].mean;
				for (int i = 0; i < columns.Length - 1; i++) {
					metadataXml.WriteStartElement( "column", fileloc );
					metadataXml.WriteAttributeString( "name", null, columns[i] );

					metadataXml.WriteStartElement( "parameter", fileloc );
					metadataXml.WriteAttributeString( "name", null, "min" );
					metadataXml.WriteString( "" + min[i] );
					metadataXml.WriteEndElement();

					metadataXml.WriteStartElement( "parameter", fileloc );
					metadataXml.WriteAttributeString( "name", null, "max" );
					metadataXml.WriteString( "" + max[i] );
					metadataXml.WriteEndElement();

					metadataXml.WriteStartElement( "parameter", fileloc );
					metadataXml.WriteAttributeString( "name", null, "mean" );
					metadataXml.WriteString( "" + mean[i] );
					metadataXml.WriteEndElement();
					metadataXml.WriteEndElement();
				}

				metadataXml.WriteEndElement();
				//--------------------------------PAYLOAD----------------------------------------------
				metadataXml.WriteStartElement( "payload", fileloc );
				metadataXml.WriteAttributeString( "location", null, payload[1].location );
				metadataXml.WriteAttributeString( "version", null, "1.0" );
				metadataXml.WriteAttributeString( "type", null, "csv" );

				columns = payload[1].columns;
				min = payload[1].min;
				max = payload[1].max;
				mean = payload[1].mean;
				for (int j = 0; j < columns.Length; j++) {
					metadataXml.WriteStartElement( "column ", fileloc );
					metadataXml.WriteAttributeString( "name", null, columns[j] );

					metadataXml.WriteStartElement( "parameter", fileloc );
					metadataXml.WriteAttributeString( "name", null, "min" );
					metadataXml.WriteString( min[j] + "" );
					metadataXml.WriteEndElement();

					metadataXml.WriteStartElement( "parameter", fileloc );
					metadataXml.WriteAttributeString( "name", null, "max" );
					metadataXml.WriteString( max[j] + "" );
					metadataXml.WriteEndElement();

					metadataXml.WriteStartElement( "parameter", fileloc );
					metadataXml.WriteAttributeString( "name", null, "mean" );
					metadataXml.WriteString( "" + mean[j] );
					metadataXml.WriteEndElement();
					metadataXml.WriteEndElement();
				}
				metadataXml.WriteEndElement();
				metadataXml.WriteEndDocument();
			} finally {
				metadataXml.Close();
			}
		}
	}

}